package msu.ml.util;

public class GeoLocationUtility
{
   protected static final double A = 6378137.0;
   protected static final double B = 6356752.314;
   protected static final double E = 0.081819191;
   protected static final double E2 = 0.00669437999014;


   public double findDistance(Location site, Location obs, double angle)
   {
      GeoModel model = constructModel(site, obs, angle);
      return model.distance;
   }

   public double findBeamAltitude(Location site, Location obs, double angle)
   {
      GeoModel model = constructModel(site, obs, angle);
      return model.beamAltitude;
   }

   public double findBeamBearing(Location site, Location obs, double angle)
   {
      GeoModel model = constructModel(site, obs, angle);
      return model.bearing;
   }

   protected GeoModel constructModel(Location site, Location obs, double angle)
   {
      GeoModel m = new GeoModel();

      m.deltaPhi = Math.abs(site.getLatitude() - obs.getLatitude());
      m.deltaMu = Math.abs(site.getLongitude() - obs.getLongitude());

      m.deltaOD = Math.abs(site.getOD() - obs.getOD());
      m.deltaDP = Math.abs(site.getDP() - obs.getDP());

      m.deltaDLat = Math.sqrt(m.deltaOD*m.deltaOD + m.deltaDP*m.deltaDP);
      m.deltaDLon = Math.sqrt((2.0 * obs.getOD() * obs.getOD())*(1.0 - Math.cos(Math.toRadians(m.deltaMu))));
      m.deltaDDiag = Math.sqrt(m.deltaDLat*m.deltaDLat + m.deltaDLon*m.deltaDLon);

      m.angleDiag = Math.asin((m.deltaDDiag * Math.sin(Math.PI / 2.0 + Math.toRadians(angle))) / obs.getOP());

      m.distance = m.deltaDDiag / 1000.0;

      m.eHeight = site.getOrthoHeight() + site.getGeoidHeight();
      m.rs = site.getOP() + m.eHeight;
      m.r188 = (m.rs * Math.sin(Math.PI / 2.0 + Math.toRadians(angle))) /
         (Math.sin(Math.PI / 2.0 - Math.toRadians(angle) - m.angleDiag));
      m.eBeamHeight = m.r188 - obs.getOP();
      m.beamAltitude = m.eBeamHeight - (obs.getOrthoHeight() + obs.getGeoidHeight());
      m.oBeamHeight = m.eBeamHeight - obs.getGeoidHeight();

      m.deltaPhi = obs.getLatitude() - site.getLatitude();
      m.deltaMu = obs.getLongitude() - site.getLongitude();

      double psi = Math.toDegrees(Math.asin(Math.abs(m.deltaDLon / m.deltaDDiag)));

      if(m.deltaPhi < 0 && m.deltaMu >= 0)
      {
         m.bearing = 180.0 - psi;
      }
      else if(m.deltaPhi <= 0 && m.deltaMu < 0)
      {
         m.bearing = 180.0 + psi;
      }
      else if(m.deltaPhi > 0 && m.deltaMu < 0)
      {
         m.bearing = 360.0 - psi;
      }
      else
      {
         m.bearing = psi;
      }

      return m;
   }
   
   protected class GeoModel
   {
      public double deltaPhi;
      public double deltaMu;
      public double deltaOD;
      public double deltaDP;
      public double deltaDLat;
      public double deltaDLon;
      public double deltaDDiag;
      public double angleDiag;
      public double distance;
      public double eHeight;
      public double rs;
      public double r188;
      public double eBeamHeight;
      public double oBeamHeight;
      public double beamAltitude;
      public double bearing;
   }
}

